import { html } from 'lit';
import { ifDefined } from 'lit/directives/if-defined.js';
import {
  Meta,
  Canvas,
  ArgsTable,
  Story,
} from '@storybook/addon-docs';
import { userEvent } from '@storybook/testing-library';
import { centeredLayout } from '../../utilities/chromatic-decorators';

<Meta
  title="Components/Select/Select"
  component="bl-select"
  parameters={{
    chromatic: { viewports: [1000] },
  }}
  decorators={[
    centeredLayout,
  ]}
  argTypes={{
    label: {
      control: 'text'
    },
    placeholder: {
      control: 'text'
    },
    size: {
      control: {
        type: 'select',
        options: ['small', 'medium', 'large']
      }
    },
    required: {
      control: 'boolean'
    },
    clearable: {
      control: 'boolean'
    },
    disabled: {
      control: 'boolean'
    },
    labelFixed: {
      control: 'boolean'
    },
    helpText: {
      control: 'text'
    },
    value: {
      control: 'text'
    },
    valueProp: {
      control: 'multi-select',
      options: defaultOptions.map((option) => option.value)
    }
  }}
/>

export const defaultOptions = [{
  label: 'Turkiye',
  value: 'tr'
},{
  label: 'The Netherlands',
  value: 'nl'
},{
  label: 'Germany',
  value: 'de',
},{
  label: 'United Kingdom',
  value: 'uk'
}]

export const defaultOptionsWithDisabled = [{
  label: 'Turkiye',
  value: 'tr'
},{
  label: 'The Netherlands',
  value: 'nl'
},{
  label: 'Germany',
  value: 'de',
  disabled: true
},{
  label: 'United Kingdom',
  value: 'uk'
}]

export const formSubmitter = async ({ canvasElement }) => {
  const form = canvasElement?.querySelector('form');
  form.addEventListener('submit', (e) => e.preventDefault());
  const submitter = form.querySelector('bl-button[type=submit]');
  if(submitter.shadowRoot) {
    const innerButton = submitter.shadowRoot.querySelector('.button')
    await userEvent.click(innerButton);
  }
}


export const selectOpener = async ({ canvasElement }) => {
  const select = canvasElement?.querySelector('bl-select')
  if(select.shadowRoot) {
    const selectInput = select.shadowRoot.querySelector('.select-input')
    await userEvent.click(selectInput);
  }
}

export const SpecialUseCaseTemplate = () => html`<bl-select style="width: 50%" placeholder="Usage with custom bl-select-options">
  <bl-select-option value="1" label="Custom Label 1">Option 1</bl-select-option>
  <bl-select-option value="2" label="Option 2">
    <div style="display: flex; align-items: center; column-gap: 4px;">
      Option 2
      <bl-tooltip placement="bottom">
        <bl-button slot="tooltip-trigger" icon="check_fill" variant="tertiary" kind="success">Recommended</bl-button>
        Some tooltip text
      </bl-tooltip>
    </div>
  </bl-select-option>
  <bl-select-option value="3">Option 3</bl-select-option>
</bl-select>`;

export const SelectTemplate = (args) => html`<bl-select
    label=${ifDefined(args.label)}
    class=${ifDefined(args.class)}
    ?label-fixed=${args.labelFixed}
    ?multiple=${args.multiple}
    ?clearable=${args.clearable}
    ?required=${args.required}
    ?disabled=${args.disabled}
    ?success=${args.success}
    ?view-select-all=${args.viewSelectAll}
    select-all-text=${ifDefined(args.selectAllText)}
    ?search-bar=${args.searchBar}
    ?search-bar-loading-state=${args.searchBarLoadingState}
    search-bar-placeholder=${ifDefined(args.searchBarPlaceholder)}
    size=${ifDefined(args.size)}
    help-text=${ifDefined(args.helpText)}
    invalid-text=${ifDefined(args.customInvalidText)}
    placeholder=${ifDefined(args.placeholder)}
    .value=${ifDefined(args.value)}>${
  (args.options || defaultOptions).map((option) => html`
  <bl-select-option value=${ifDefined(option.value)} ?selected=${
    ( args.selected || []).includes(option.value) } ?disabled=${option.disabled}>${option.label}</bl-select-option>`
  )}
</bl-select>`

export const FormTemplate = (args) => html`<form class="stacked-form" novalidate>
${SelectTemplate(args)}
<bl-button type="submit">Submit</bl-button>
</form>
`

export const SelectWithStyleTemplate = (args) => html`
<style>
.${args.class} {
  width: ${args.width};
${ args.maxWidth ? `  max-width: ${args.maxWidth};\n` : ''}}
</style>
${SelectTemplate(args)}
`

# Select

<bl-badge icon="document">[ADR](https://github.com/Trendyol/baklava/issues/88)</bl-badge>
<bl-badge icon="document">[Accessibility](https://github.com/Trendyol/baklava/issues/686#issuecomment-2079522703)</bl-badge>
<bl-badge icon="puzzle">[Figma](https://www.figma.com/file/RrcLH0mWpIUy4vwuTlDeKN/Baklava-Design-Guide?node-id=25%3A3606)</bl-badge>
<bl-badge icon="check_fill">RTL Supported</bl-badge>

Select component is a component for selecting a value from a list of options.
Each option should be wrapped with `bl-select-option` component.

## Basic Usage

<Canvas>
  <Story name="Basic Usage" args={{ placeholder: "Choose country", }} play={selectOpener}>
    {SelectTemplate.bind({})}
  </Story>
  <Story name="Select with initial value" args={{ placeholder: "Choose country", value: 'tr' }} play={selectOpener}>
    {SelectTemplate.bind({})}
  </Story>
  <Story name="Select With Selected Option" args={{ options: defaultOptionsWithDisabled, placeholder: "Choose country", selected: ['nl']  }} play={selectOpener}>
    {SelectTemplate.bind({})}
  </Story>
</Canvas>

## Multiple Select

There will be checkboxes in select menu when `multiple` attribute is set to true.
Selected options will be visible on input seperated by commas.
<Canvas>
  <Story name="Multiple Select" args={{ placeholder: "Choose countries", multiple: true }} play={selectOpener}>
    {SelectTemplate.bind({})}
  </Story>
  <Story name="Multiple Select initial selection" args={{ placeholder: "Choose countries", value: ['nl'], multiple: true  }} play={selectOpener}>
    {SelectTemplate.bind({})}
  </Story>
</Canvas>

## Select All

The Select component features a 'Select All' option in the Multiple Select, which is displayed when the `view-select-all` attribute is enabled. The text for the 'Select All' option can be customized by using the `select-all-text` attribute. Additionally, 'Select All' feature will not have any effect on disabled options.

<Canvas>
  <Story
    name="Select All"
    args={{ placeholder: "Choose countries", multiple: true, viewSelectAll: true, selectAllText: 'Select All Countries', options: [{
      label: 'United States',
      value: 'us',
    }, ...defaultOptions]  }}
    play={selectOpener}
  >
    {SelectTemplate.bind({})}
  </Story>
  <Story
    name="Select All with Disabled Options"
    args={{ placeholder: "Choose countries", multiple: true, viewSelectAll: true, selectAllText: 'Select All Countries', options: [{
      label: 'United States',
      value: 'us',
      disabled: true
    }, ...defaultOptions]  }}
    play={selectOpener}
  >
    {SelectTemplate.bind({})}
  </Story>
  <Story name="Select All Hidden" args={{ placeholder: "Choose countries", value: ['nl'], multiple: true }} play={selectOpener}>
    {SelectTemplate.bind({})}
  </Story>
</Canvas>

Select all is designed to operate on filtered results as well, selecting only those options that meet the filter.

<Canvas>
  <Story name="Searchable with Select All" args={{ searchBar: true, searchBarPlaceholder: 'Search your options', multiple: true, viewSelectAll: true }}>
    {SelectTemplate.bind({})}
  </Story>
</Canvas>

## Clear Button

The select component includes a clear button. Clear button can be displayed by passing `clearable` attribute to the Select component.

<Canvas>
  <Story name="Select With Clear Button" args={{ placeholder: "Choose country", value: 'tr', clearable: true }} play={selectOpener}>
    {SelectTemplate.bind({})}
  </Story>
  <Story name="Multiple Select With Clear Button" args={{ placeholder: "Choose countries", value: ['nl'], multiple: true, clearable: true  }} play={selectOpener}>
    {SelectTemplate.bind({})}
  </Story>
</Canvas>

## Select Labels

Select component optionally can have a `label`. If the label is set, it will be a floating label by default. If you want to use always it on top of the input, then you can use `label-fixed` attribute.

<Canvas isColumn>
  <Story name="Select With Label" args={{ label: "Country" }}>
    {SelectTemplate.bind({})}
  </Story>
  <Story name="Select With Fixed Label" args={{ label: "Country", placeholder:"Choose country", labelFixed: true }}>
    {SelectTemplate.bind({})}
  </Story>
  <Story name="Select Without Label" args={{ placeholder: "Choose country" }}>
    {SelectTemplate.bind({})}
  </Story>
</Canvas>

Select component will cut-out long labels and placeholders those doesn't width of select, with ellipsis char.


<Canvas isColumn>
  <Story name="Select With Long Label" args={{ label: "Very very long label that doesn't fit select component width" }}>
    {SelectTemplate.bind({})}
  </Story>
  <Story name="Select With Fixed Long Label" args={{ label: "Very very long label that doesn't fit select component width", placeholder:"Choose country", labelFixed: true }}>
    {SelectTemplate.bind({})}
  </Story>
  <Story name="Select Without Label with Long Placeholder" args={{ placeholder: "Very very long placeholder that doesn't fit select component width" }}>
    {SelectTemplate.bind({})}
  </Story>
</Canvas>

## Disabled State

Select component can be disabled by using `disabled` attribute.

<Canvas>
  <Story name="Disabled Select" args={{ placeholder: "Choose country", disabled: true }}>
    {SelectTemplate.bind({})}
  </Story>
  <Story name="Disabled Select with initial value" args={{ placeholder: "Choose country", value: 'nl', disabled: true }}>
    {SelectTemplate.bind({})}
  </Story>
</Canvas>

## Searchable

<bl-badge icon="document">[ADR](https://github.com/Trendyol/baklava/issues/265#issuecomment-1845414216)</bl-badge>

Select component can be searchable by using `search-bar` attribute.

Display a loading icon in place of the search icon with using `search-bar-loading-state` attribute, seamlessly integrating it with your API endpoint to provide a smooth user experience during data retrieval.

<Canvas>
  <Story name="Searchable" args={{ searchBar: true, searchBarPlaceholder: 'Search your options' }}>
    {SelectTemplate.bind({})}
  </Story>

  <Story name="Searchable with Multiple Option" args={{ searchBar: true,  searchBarPlaceholder: 'Search your options', multiple: true, viewSelectAll: true }}>
    {SelectTemplate.bind({})}
  </Story>

  <Story name="Searchable with Loading State" args={{ searchBar: true, searchBarPlaceholder: 'Search your options', multiple: true, searchBarLoadingState: true }}>
    {SelectTemplate.bind({})}
  </Story>

  <Story name="Searchable with Label" args={{ searchBar: true, searchBarPlaceholder: 'Search your options', label: 'Label' }}>
    {SelectTemplate.bind({})}
  </Story>

  <Story name="Searchable with Fixed Label" args={{ searchBar: true, searchBarPlaceholder: 'Search your options', label: 'Label', labelFixed: true }}>
    {SelectTemplate.bind({})}
  </Story>
</Canvas>

## Special Use Cases

Select component can render a custom option label if `bl-select-option` has a `label` attribute.

This is useful when you want to show a custom label for an option inside a `bl-select`, but you want to show original content inside the option itself.

<Canvas>
  <Story name="Special Use Cases">
    {SpecialUseCaseTemplate.bind({})}
  </Story>
</Canvas>

## `bl-select` Event

Select component fires `bl-select` event once selection changes. This event has a payload in the type of
`CustomEvent<ISelectOption<ValueType>[]>` which `ValueType` is the generic comes from `BlSelect<ValueType = string>`.
`ISelectOption` interface consists of:

```ts
interface ISelectOption<T> {
  value: T;
  text: string;
  selected: boolean;
}
```

So once you get a `bl-select` event, event object is a [`CustomEvent`](https://developer.mozilla.org/en-US/docs/Web/API/CustomEvent/CustomEvent)
that always has an array of `ISelectOption` as `detail` property.


## Using as form input

Select component can be used inside HTML Forms. You can set `name` attribute to attach select value
to form.

```html
<form method="get" action="/survey" novalidate>
  <bl-select name="country" label="Country" help-text="Please select the country you live in" required>
    <bl-select-option value="tr">Turkiye</bl-select-option>
    <bl-select-option value="nl">The Netherlands</bl-select-option>
  </bl-select>

  <bl-button type="submit">Submit</bl-button>
</form>
```

When you submit form above from value will be sent to the server as `?country=tr` values. If you use with `multiple`
attributes, values will be sent as `?country=tr&country=nl` (as same as native select and checkbox inputs).

## Using with complex values

HTML form fields only handles string values (if we exclude file uploads). But Select component can work with complex
values with the help of Javascript. Altough `value` attribute only accepts `string` format, `value` **property**
accepts arbitrary types (Same applies to `bl-select-option`'s `value`). Check example below:

```html
<form action="/product-select" method="get" novalidate>
  <bl-select id="productSelect" name="product" label="Products" multiple></bl-select>

  <bl-button type="submit">Submit</bl-button>
</form>

<bl-button id="addProduct">Add Random Product</bl-button>

<script>
document.getElementById('addProduct').addEventListener('bl-click', () => {
  const id = Math.floor(Math.random() * 1000);
  const product = {
    id,
    name: `Product ${id}`,
    price: `$${id}`,
    toString() {
      return this.id;
    }
  };

  const option = document.createElement('bl-select-option');
  option.value = product;
  option.textContent = product.name;
  document.getElementById('productSelect').appendChild(option);
});

document.getElementById('productSelect').addEventListener('bl-select', (e) => {
  console.log(e.detail.value);
});
</script>
```

In example above, we add dynamic options to our product select. As you can see option values are
full product objects that has multiple properties. When you make a selection, you'll notice that
you get list selected of objects. If you submit this selection as an HTML form, since an object can
not be used as a form value, `toString` method of the object will be used. In this example we return
`id` property of the object in our `toString` method.

As a more advanced usage with TypeScript, you can use generic types to set the type of BlSelect value.

```ts
interface IProduct {
  id: number;
  name: string;
  price: number;
}

const option = document.createElement<BlSelectOption<IProduct>>('bl-select-option');
option.value = product;

...

document.getElementById<BlSelect<IProduct>>('productSelect')
  .addEventListener('bl-select', (e: CustomEvent<ISelectOption<IProduct>[]>) => {
    console.log(e.detail[0].value); // Will be a product
  })
```

## Select Validation

Select component supports `required` rule to do validation.

Select validation will run after user selects a value and go out from the input, or wrapping form submits.
If there is a validation issue, select will be highlighted in error state. After this state every change
will have immediate effect on input to update validation state.

<Canvas>
  <Story name="Validation with Select" args={{ label: 'Choose country', labelFixed: true, required: true }} play={formSubmitter}>
    {FormTemplate.bind({})}
  </Story>
</Canvas>

You can set an error message by setting `invalid-text` attribute.

<Canvas>
  <Story name="Custom Error Message" args={{ label: 'Choose country', labelFixed: true, required: true, customInvalidText: 'This field is mandatory' }} play={formSubmitter}>
    {FormTemplate.bind({})}
  </Story>
</Canvas>

## Select Help Text

You can give extra information to user with `help-text` attribute.

<Canvas>
  <Story name="Select Help Text" args={{ helpText: 'You should select your country' }}>
    {SelectTemplate.bind({})}
  </Story>
</Canvas>

## Select Sizes

Select have 3 size options: `small`, `medium` and `large`. `medium` size is default and if you want to show select in another size you can set `size` attribute to `large` or `small`.

<Canvas>
  <Story name="Small Select" args={{ label: 'Choose country', size: 'small' }}>
    {SelectTemplate.bind({})}
  </Story>
  <Story name="Medium Select" args={{ label: 'Choose country' }}>
    {SelectTemplate.bind({})}
  </Story>
  <Story name="Large Select" args={{ label: 'Choose country', size: 'large' }}>
    {SelectTemplate.bind({})}
  </Story>
</Canvas>

## Select with auto width

Select can be used with auto width. If you want to use select with auto width, you can set `width: auto` to the host element.

<Canvas>
  <Story name="Select Auto Width" args={{ label: 'Choose country', class: 'country-selector', width: 'auto' }}>
    {SelectWithStyleTemplate.bind({})}
  </Story>
  <Story name="Select Auto Width default value" args={{ label: 'Choose country', value: 'de', class: 'country-selector-with-value', width: 'auto' }}>
    {SelectWithStyleTemplate.bind({})}
  </Story>
  <Story name="Select Auto Width multiple" args={{ label: 'Choose country', multiple: true, value: ['tr','nl'], class: 'country-selector-multiple', width: 'auto' }}>
    {SelectWithStyleTemplate.bind({})}
  </Story>
  <Story name="Select Auto Width multiple max-width" args={{ label: 'Choose country', multiple: true, value: ['tr','nl','de'], class: 'country-selector-multiple-many', width: 'auto', maxWidth: '150px' }}>
    {SelectWithStyleTemplate.bind({})}
  </Story>
</Canvas>

## RTL Support

The select component supports RTL (Right-to-Left) text direction. You can enable RTL mode by setting the `dir` attribute on a parent element or the `html` tag.

<Canvas>
  <Story name="RTL Support">
    {() => html`
      <div style="display: flex; gap: 16px;">
        <div>
          <p style="margin-bottom: 8px;">LTR (Left-to-Right)</p>
          <bl-select label="Country" placeholder="Select a country">
            <bl-select-option value="us">United States</bl-select-option>
            <bl-select-option value="uk">United Kingdom</bl-select-option>
            <bl-select-option value="fr">France</bl-select-option>
          </bl-select>
        </div>
        <div dir="rtl">
          <p style="margin-bottom: 8px;">RTL (Right-to-Left)</p>
          <bl-select label="الدولة" placeholder="اختر دولة">
            <bl-select-option value="us">الولايات المتحدة</bl-select-option>
            <bl-select-option value="uk">المملكة المتحدة</bl-select-option>
            <bl-select-option value="fr">فرنسا</bl-select-option>
          </bl-select>
        </div>
      </div>
    `}
  </Story>
</Canvas>

Here's a vanilla JavaScript and HTML example of how to use RTL support:

```html
<!-- index.html -->
<html>
<head>
  <meta charset="UTF-8">
  <title>Baklava Select RTL Example</title>
  <!-- Import Baklava's CSS and JavaScript -->
  <script type="module" src="path/to/baklava.js"></script>
  <style>
    .container {
      margin: 20px;
    }
    .flex-container {
      display: flex;
      gap: 20px;
      margin-top: 20px;
    }
    .select-group {
      flex: 1;
    }
  </style>
</head>
<body>
  <div class="container">
    <button onclick="toggleDirection()">Toggle RTL/LTR</button>

    <div class="flex-container">
      <!-- LTR Example -->
      <div class="select-group">
        <h3>LTR Select</h3>
        <bl-select label="Country" placeholder="Select a country">
          <bl-select-option value="us">United States</bl-select-option>
          <bl-select-option value="uk">United Kingdom</bl-select-option>
          <bl-select-option value="fr">France</bl-select-option>
        </bl-select>
      </div>

      <!-- RTL Example -->
      <div class="select-group" dir="rtl">
        <h3>RTL Select</h3>
        <bl-select label="الدولة" placeholder="اختر دولة">
          <bl-select-option value="us">الولايات المتحدة</bl-select-option>
          <bl-select-option value="uk">المملكة المتحدة</bl-select-option>
          <bl-select-option value="fr">فرنسا</bl-select-option>
        </bl-select>
      </div>
    </div>
  </div>

  <script>
    // Example of dynamically changing direction
    const toggleDirection = () => {
      const rtlContainer = document.querySelector('[dir="rtl"]');
      rtlContainer.dir = rtlContainer.dir === 'rtl' ? 'ltr' : 'rtl';
    };

    // Example of creating a select programmatically
    const createSelect = (isRTL) => {
      const container = document.createElement('div');
      if (isRTL) container.dir = 'rtl';

      const select = document.createElement('bl-select');
      select.label = isRTL ? 'الدولة' : 'Country';
      select.placeholder = isRTL ? 'اختر دولة' : 'Select a country';

      const option = document.createElement('bl-select-option');
      option.value = 'us';
      option.textContent = isRTL ? 'الولايات المتحدة' : 'United States';
      select.appendChild(option);

      container.appendChild(select);
      document.body.appendChild(container);
    };
  </script>
</body>
</html>
```

## Reference

<ArgsTable of="bl-select" />
